package cn.demoncat.util.ocr.baidu;

import cn.demoncat.util.exception.BizErrorRuntimeException;
import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.io.ImgUtil;
import cn.demoncat.util.io.IoUtil;
import cn.demoncat.util.lang.LocalDateUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.constant.card.BankCardType;
import cn.demoncat.util.lang.constant.card.BusinessLicenseType;
import cn.demoncat.util.lang.entity.card.BankCard;
import cn.demoncat.util.lang.entity.card.BusinessLicense;
import cn.demoncat.util.lang.entity.card.IdCard;
import cn.demoncat.util.ocr.baidu.config.BdOcrProperties;
import cn.demoncat.util.ocr.baidu.constant.BdOcrApi;
import cn.demoncat.util.ocr.baidu.constant.BdOcrConstant;
import cn.demoncat.util.web.HttpClientUtil;
import com.baidu.aip.ocr.AipOcr;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 图像识别：百度 OCR
 *
 * @author 延晓磊
 * @since 2020年10月25日
 */
@Component
public class BdOcrUtil {

    // 百度云-OCR客户端
    private final AipOcr client;

    /**
     * 图片限制：Base64+UrlEncode = 4M，原图大约3M
     *
     * 注：部分图片会压缩失败，比如.bmp
     */
    private static final long FILE_MAX = 3 * 1000 * 1000;
    
    private static final Logger LOG = LoggerFactory.getLogger(BdOcrUtil.class);

    @Autowired
    public BdOcrUtil(BdOcrProperties bdOcrProperties) {
        this.client = new AipOcr(bdOcrProperties.getAppId(), bdOcrProperties.getApiKey(), bdOcrProperties.getApiPassword());
    }

    /**
     * 身份证识别
     *
     * @param file
     * @param side  正面true,反面false
     * @return
     * 
     * @author 延晓磊
     * @since 2020年10月26日
     */
    public IdCard idCard(byte[] file, boolean side){
        // 图片压缩
        if (file.length > FILE_MAX) {
            file = ImgUtil.zoom(IoUtil.getInputStream(file), FILE_MAX);
        }
        // 发起请求
        return parseIdCard(client.idcard(file, side ? "front" : "back", null),side);
    }

    /**
     * 身份证识别
     *
     * @param file  图片路径/URL
     * @param side  正面true,反面false
     * @return
     * 
     * @author 延晓磊
     * @since 2020年10月26日
     */
    public IdCard idCard(String file, boolean side){
        if (file.startsWith("http://") || file.startsWith("https://")) {
            // 远程图片
            return idCard(HttpClientUtil.doGetBytes(file), side);
        } else {
            // 本地图片
            return parseIdCard(client.idcard(file, side ? "front" : "back", null),side);
        }
    }

    /**
     * 银行卡识别
     *
     * @param file
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    public BankCard bankCard(byte[] file){
        // 图片压缩
        if (file.length > FILE_MAX) {
            file = ImgUtil.zoom(IoUtil.getInputStream(file), FILE_MAX);
        }
        // 发起请求
        return parseBankCard(client.bankcard(file, null));
    }

    /**
     * 银行卡识别
     *
     * @param file  图片路径/URL
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    public BankCard bankCard(String file){
        if (file.startsWith("http://") || file.startsWith("https://")) {
            // 远程图片
            return bankCard(HttpClientUtil.doGetBytes(file));
        } else {
            // 本地图片
            return parseBankCard(client.bankcard(file, null));
        }
    }

    /**
     * 营业执照识别
     *
     * @param file
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    public BusinessLicense businessLicense(byte[] file){
        // 图片压缩
        if (file.length > FILE_MAX) {
            file = ImgUtil.zoom(IoUtil.getInputStream(file), FILE_MAX);
        }
        // 发起请求
        return parseBusinessLicense(client.businessLicense(file, null));
    }

    /**
     * 营业执照识别
     *
     * @param file  图片路径/URL
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    public BusinessLicense businessLicense(String file){
        if (file.startsWith("http://") || file.startsWith("https://")) {
            // 远程图片
            return businessLicense(HttpClientUtil.doGetBytes(file));
        } else {
            // 本地图片
            return parseBusinessLicense(client.businessLicense(file, null));
        }
    }

    /**
     * 解析身份证识别
     *
     * @param data  结果
     * @param side  正面true，反面false
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    private IdCard parseIdCard(JSONObject data, boolean side){
        // 错误码校验
        check(data, BdOcrApi.ID_CARD);
        // 解析状态校验
        String status = data.getString("image_status");
        if (!"normal".equals(status)) {
            // 成功率校验
            LOG.warn("请求百度云OCR错误：api = {}, error = 识别错误, response = {}", BdOcrApi.ID_CARD.getName(), data);
            throw new BizRuntimeException(BdOcrApi.ID_CARD.getName() + "失败：" + BdOcrConstant.ID_CARD_STATUS.get(status));
        }
        // 解析
        data = data.getJSONObject("words_result");
        IdCard result = new IdCard();
        if (side) {
            result.setName(data.getJSONObject("姓名").getString("words"));
            if (StringUtils.isBlank(result.getName())) {
                // 成功率校验
                LOG.warn("请求百度云OCR错误：api = {}, error = 识别错误, response = {}", BdOcrApi.ID_CARD.getName(), data);
                throw new BizRuntimeException(BdOcrApi.ID_CARD.getName() + "失败，请重新上传");
            }
            result.setAddress(data.getJSONObject("住址").getString("words"));
            result.setBirth(toDate(data.getJSONObject("出生").getString("words")));
            result.setEthnicity(data.getJSONObject("民族").getString("words"));
            result.setNumber(data.getJSONObject("公民身份号码").getString("words"));
            result.setSex(data.getJSONObject("性别").getString("words").equals("男") ? "1" : "2");
        }else{
            result.setIssue(data.getJSONObject("签发机关").getString("words"));
            if (StringUtils.isBlank(result.getIssue())) {
                // 成功率校验
                LOG.warn("请求百度云OCR错误：api = {}, error = 识别错误, response = {}", BdOcrApi.ID_CARD.getName(), data);
                throw new BizRuntimeException(BdOcrApi.ID_CARD.getName() + "失败，请重新上传");
            }
            result.setValidFrom(toDate(data.getJSONObject("签发日期").getString("words")));
            result.setValidTo(toDate(data.getJSONObject("失效日期").getString("words")));
        }
        return  result;
    }

    /**
     * 解析银行卡识别
     *
     * @param data  结果
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    private BankCard parseBankCard(JSONObject data){
        // 错误码校验
        check(data, BdOcrApi.BANK_CARD);
        // 解析
        data = data.getJSONObject("result");
        BankCard result = new BankCard();
        result.setBankName(data.getString("bank_name"));
        if (StringUtils.isBlank(result.getBankName())) {
            // 成功率校验
            LOG.warn("请求百度云OCR错误：api = {}, error = 识别错误, response = {}", BdOcrApi.BANK_CARD.getName(), data);
            throw new BizRuntimeException(BdOcrApi.BANK_CARD.getName() + "失败，请重新上传");
        }
        result.setCardNumber(StringUtil.remove(data.getString("bank_card_number")));
        result.setBankName(BankCardType.CREDIT.getType().equals(data.getString("bank_card_type")) ? BankCardType.CREDIT.getType() : BankCardType.DEBIT.getType());
        result.setExpiryDate(data.getString("valid_date"));
        result.setExpiryDate("20"+result.getExpiryDate().substring(3,5)+"-"+result.getExpiryDate().substring(0,2)+"-01");
        return result;
    }

    /**
     * 解析营业执照识别
     *
     * @param data  结果
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月26日
     */
    private BusinessLicense parseBusinessLicense(JSONObject data){
        // 错误码校验
        check(data, BdOcrApi.BUSINESS_LICENSE);
        // 解析
        data = data.getJSONObject("words_result");
        BusinessLicense result = new BusinessLicense();
        result.setRegistrationNumber(data.getJSONObject("社会信用代码").getString("words"));
        if (StringUtils.isBlank(result.getRegistrationNumber())) {
            // 成功率校验
            LOG.warn("请求百度云OCR错误：api = {}, error = 识别错误, response = {}", BdOcrApi.BUSINESS_LICENSE.getName(), data);
            throw new BizRuntimeException(BdOcrApi.BUSINESS_LICENSE.getName() + "失败，请重新上传");
        }
        result.setName(data.getJSONObject("单位名称").getString("words"));
        result.setType(BusinessLicenseType.getByName(data.getJSONObject("类型").getString("words")).getType());
        result.setAddress(data.getJSONObject("地址").getString("words"));
        result.setLegalRepresentative(data.getJSONObject("法人").getString("words"));
        result.setRegisteredCapital(data.getJSONObject("注册资本").getString("words"));
        result.setFoundDate(data.getJSONObject("成立日期").getString("words"));
        result.setBusinessTerm(data.getJSONObject("有效期").getString("words"));
        result.setBusinessScope(data.getJSONObject("经营范围").getString("words"));
        return result;
    }

    /**
     * 响应校验
     * 
     * @param data
     * @param api
     * @return
     * 
     * @author 延晓磊
     * @since 2020年10月26日
     */
    private void check(JSONObject data, BdOcrApi api){
        if (data.has("error_code")) {
            String msg = BdOcrConstant.ERRORS.get(StringUtil.toString(data.get("error_code")));
            String log = StringUtil.logFormat("请求百度云OCR失败：api = {}, response = {}", api.getName(), data);
            if (StringUtils.isNotBlank(msg)) {
                LOG.warn(log);
                throw new BizRuntimeException(api.getName() + "失败：" + msg);
            }else{
                LOG.error(log);
                throw new BizErrorRuntimeException(api.getName() + "失败，请重新上传");
            }
        }
    }

    /**
     * 日期格式化
     *
     * @param date
     * @return
     *
     * @author 延晓磊
     * @since 2020年11月03日
     */
    private static String toDate(String date){
        if(date.endsWith("日")){
           return LocalDateUtil.toStr(LocalDateUtil.toLocalDate(date, DateConstant.DATE_FORMAT_ZH));
        }else if (StringUtils.isNumeric(date)) {
            return LocalDateUtil.toStr(LocalDateUtil.toLocalDate(date, DateConstant.DATE_FORMAT_NUM));
        }else{
            return date;
        }
    }

}
